package com.wande.dataplatform.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wande.common.core.exception.ServiceException;
import com.wande.common.core.utils.MapstructUtils;
import com.wande.common.encrypt.utils.EncryptUtils;
import com.wande.common.mybatis.core.page.PageQuery;
import com.wande.common.mybatis.core.page.TableDataInfo;
import com.wande.dataplatform.common.constant.DataPlatformConstants;
import com.wande.dataplatform.common.enums.DataPlatformErrorCode;
import com.wande.dataplatform.common.utils.DataSourceUtils;
import com.wande.dataplatform.domain.DataSource;
import com.wande.dataplatform.domain.bo.DataSourceBo;
import com.wande.dataplatform.domain.vo.DataSourceVo;
import com.wande.dataplatform.mapper.DataSourceMapper;
import com.wande.dataplatform.service.IDataSourceService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;

/**
 * 数据源Service业务层处理
 *
 * @author ruoyi
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class DataSourceServiceImpl implements IDataSourceService {

    private final DataSourceMapper baseMapper;

    /**
     * AES加密密钥（从配置文件读取）
     * 注意：AES密钥长度必须是16、24或32位
     */
    @Value("${dataplatform.datasource.encrypt-key:DataPlatform16!!}")
    private String encryptKey;

    /**
     * 查询数据源
     * 注意：租户隔离由 MyBatis-Plus TenantLineHandler 自动处理
     */
    @Override
    public DataSourceVo queryById(Long id) {
        DataSourceVo vo = baseMapper.selectVoById(id);
        if (vo == null) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getMessage(),
                DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getCode());
        }
        // 不返回密码
        return vo;
    }

    /**
     * 查询数据源列表
     */
    @Override
    public TableDataInfo<DataSourceVo> queryPageList(DataSourceBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<DataSource> lqw = buildQueryWrapper(bo);
        Page<DataSourceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询数据源列表
     */
    @Override
    public List<DataSourceVo> queryList(DataSourceBo bo) {
        LambdaQueryWrapper<DataSource> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    /**
     * 构建查询条件
     * 注意：租户ID过滤由 MyBatis-Plus TenantLineHandler 自动添加，无需手动处理
     */
    private LambdaQueryWrapper<DataSource> buildQueryWrapper(DataSourceBo bo) {
        LambdaQueryWrapper<DataSource> lqw = Wrappers.lambdaQuery();
        lqw.like(StrUtil.isNotBlank(bo.getName()), DataSource::getName, bo.getName());
        lqw.eq(StrUtil.isNotBlank(bo.getType()), DataSource::getType, bo.getType());
        lqw.eq(bo.getStatus() != null, DataSource::getStatus, bo.getStatus());
        lqw.eq(bo.getProjectId() != null, DataSource::getProjectId, bo.getProjectId());
        lqw.orderByDesc(DataSource::getCreateTime);
        return lqw;
    }

    /**
     * 新增数据源
     */
    @Override
    public Boolean insertByBo(DataSourceBo bo) {
        // 校验数据源名称唯一性
        if (!checkNameUnique(bo)) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_ALREADY_EXISTS.getMessage(),
                DataPlatformErrorCode.DATASOURCE_ALREADY_EXISTS.getCode());
        }

        DataSource add = MapstructUtils.convert(bo, DataSource.class);
        
        // 加密密码
        if (StrUtil.isNotBlank(add.getPassword())) {
            add.setPassword(encryptPassword(add.getPassword()));
        }
        
        // 设置默认端口
        if (add.getPort() == null) {
            add.setPort(DataSourceUtils.getDefaultPort(add.getType()));
        }
        
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改数据源
     */
    @Override
    public Boolean updateByBo(DataSourceBo bo) {
        // 校验数据源是否存在
        DataSource dataSource = baseMapper.selectById(bo.getId());
        if (dataSource == null) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getMessage(),
                DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getCode());
        }

        // 校验数据源名称唯一性
        if (!checkNameUnique(bo)) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_ALREADY_EXISTS.getMessage(),
                DataPlatformErrorCode.DATASOURCE_ALREADY_EXISTS.getCode());
        }

        DataSource update = MapstructUtils.convert(bo, DataSource.class);
        
        // 如果密码有变化，则加密新密码
        if (StrUtil.isNotBlank(update.getPassword()) && 
            !update.getPassword().equals(dataSource.getPassword())) {
            update.setPassword(encryptPassword(update.getPassword()));
        }
        
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 批量删除数据源
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        // TODO: 可以添加校验逻辑，检查数据源是否被使用
        return baseMapper.deleteByIds(ids) > 0;
    }

    /**
     * 测试数据源连接
     */
    @Override
    public Boolean testConnection(Long id) {
        DataSource dataSource = baseMapper.selectById(id);
        if (dataSource == null) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getMessage(),
                DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getCode());
        }

        return testConnectionInternal(dataSource);
    }

    /**
     * 测试数据源连接（使用BO对象）
     */
    @Override
    public Boolean testConnectionByBo(DataSourceBo bo) {
        DataSource dataSource = MapstructUtils.convert(bo, DataSource.class);
        // 如果密码不为空且不是加密后的密码，则加密
        if (StrUtil.isNotBlank(dataSource.getPassword()) && 
            !dataSource.getPassword().startsWith("ENC(")) {
            dataSource.setPassword(encryptPassword(dataSource.getPassword()));
        }
        return testConnectionInternal(dataSource);
    }

    /**
     * 内部测试连接方法
     */
    private Boolean testConnectionInternal(DataSource dataSource) {
        String driverClassName = DataSourceUtils.getDriverClassName(dataSource.getType());
        if (driverClassName == null) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getMessage(),
                DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getCode());
        }

        String jdbcUrl = DataSourceUtils.buildJdbcUrl(
            dataSource.getType(),
            dataSource.getHost(),
            dataSource.getPort(),
            dataSource.getDatabaseName()
        );

        if (jdbcUrl == null) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_CONFIG_INVALID.getMessage(),
                DataPlatformErrorCode.DATASOURCE_CONFIG_INVALID.getCode());
        }

        // 解密密码
        String password = decryptPassword(dataSource.getPassword());

        try {
            Class.forName(driverClassName);
            try (Connection conn = DriverManager.getConnection(jdbcUrl, dataSource.getUsername(), password)) {
                return conn != null && !conn.isClosed();
            }
        } catch (ClassNotFoundException e) {
            log.error("数据源驱动类未找到: {}", driverClassName, e);
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getMessage(),
                DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getCode());
        } catch (SQLException e) {
            log.error("数据源连接失败: {}", e.getMessage(), e);
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_CONNECTION_FAILED.getMessage() + ": " + e.getMessage(),
                DataPlatformErrorCode.DATASOURCE_CONNECTION_FAILED.getCode());
        }
    }

    /**
     * 获取数据源连接
     */
    @Override
    public Connection getConnection(Long id) {
        DataSource dataSource = baseMapper.selectById(id);
        if (dataSource == null) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getMessage(),
                DataPlatformErrorCode.DATASOURCE_NOT_FOUND.getCode());
        }

        String driverClassName = DataSourceUtils.getDriverClassName(dataSource.getType());
        if (driverClassName == null) {
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getMessage(),
                DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getCode());
        }

        String jdbcUrl = DataSourceUtils.buildJdbcUrl(
            dataSource.getType(),
            dataSource.getHost(),
            dataSource.getPort(),
            dataSource.getDatabaseName()
        );

        // 解密密码
        String password = decryptPassword(dataSource.getPassword());

        try {
            Class.forName(driverClassName);
            return DriverManager.getConnection(jdbcUrl, dataSource.getUsername(), password);
        } catch (ClassNotFoundException e) {
            log.error("数据源驱动类未找到: {}", driverClassName, e);
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getMessage(),
                DataPlatformErrorCode.DATASOURCE_TYPE_NOT_SUPPORTED.getCode());
        } catch (SQLException e) {
            log.error("数据源连接失败: {}", e.getMessage(), e);
            throw new ServiceException(DataPlatformErrorCode.DATASOURCE_CONNECTION_FAILED.getMessage(),
                DataPlatformErrorCode.DATASOURCE_CONNECTION_FAILED.getCode());
        }
    }

    /**
     * 校验数据源名称是否唯一
     * 注意：租户级别的唯一性校验，由 TenantLineHandler 自动添加租户ID过滤条件
     */
    @Override
    public Boolean checkNameUnique(DataSourceBo bo) {
        LambdaQueryWrapper<DataSource> lqw = Wrappers.lambdaQuery();
        lqw.eq(DataSource::getName, bo.getName());
        lqw.ne(bo.getId() != null, DataSource::getId, bo.getId());
        return baseMapper.selectCount(lqw) == 0;
    }

    /**
     * 加密密码
     */
    private String encryptPassword(String password) {
        if (StrUtil.isBlank(password)) {
            return password;
        }
        try {
            return EncryptUtils.encryptByAes(password, encryptKey);
        } catch (Exception e) {
            log.error("密码加密失败", e);
            throw new ServiceException("密码加密失败");
        }
    }

    /**
     * 解密密码
     */
    private String decryptPassword(String encryptedPassword) {
        if (StrUtil.isBlank(encryptedPassword)) {
            return encryptedPassword;
        }
        try {
            return EncryptUtils.decryptByAes(encryptedPassword, encryptKey);
        } catch (Exception e) {
            log.error("密码解密失败", e);
            throw new ServiceException("密码解密失败");
        }
    }

}
